﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using Microsoft.Xna.Framework;

using Point = System.Drawing.Point;
using Color = System.Drawing.Color;

using Graphics = LevelEditor.Graphics;
namespace LevelEditor
{
    public partial class MainMenu : Form
    {
        /// <summary>
        /// Previous directories
        /// </summary>
        private Stack<DirectoryInfo> previousDirectories = new Stack<DirectoryInfo>();

        /// <summary>
        /// Random value to use EVERYWHERE!
        /// </summary>
        public static Random random = new Random();

        private static MainMenu _instance;
        /// <summary>
        /// Gets or sets the singleton instance of main menu
        /// </summary>
        public static MainMenu instance
        {
            get { return _instance; }
            set { _instance = value; }
        }

        /// <summary>
        /// The current directory the user is looking at
        /// </summary>
        DirectoryInfo currentDirectory = new DirectoryInfo(Environment.CurrentDirectory);

        /// <summary>
        /// Icons for the tree view
        /// </summary>
        private ImageList fileIcons;

        /// <summary>
        /// Indicates if the form is currently running
        /// </summary>
        public bool Running = true;

        /// <summary>
        /// External code that loads xna content
        /// </summary>
        public MaterialEditor.ContentBuilder contentBuilder;

        /// <summary>
        /// Content manager for loading xna content
        /// </summary>
        public Microsoft.Xna.Framework.Content.ContentManager content;

        /// <summary>
        /// The window for editing the heightmap
        /// </summary>
        public HeightmapEditor heightmapEditor;

        /// <summary>
        /// Current time during the previous update
        /// </summary>
        private TimeSpan then;

        /// <summary>
        /// The list of materials for the terrain
        /// </summary>
        BindingList<TerrainMaterial> terrainMaterials
        {
            get { return viewWindow1.terrain.textures; }
        }


        private MaterialEditor.ServiceContainer services = new MaterialEditor.ServiceContainer();

        enum BrowserImageType
        {
            Folder,
            Unrecognized,
            Model,
            Texture,
        }

        enum PaintTerrainTool
        {
            increaseHeight,
            decreaseHeight,
            paintTexture,
        }

        public Graphics graphics;

        public MainMenu()
        {
            InitializeComponent();

            FormClosing += MainMenu_FormClosing;

            graphics = Graphics.Inilitize(viewWindow1);

            UpdateFileList();

            // load favorite file path names
            FileInfo favorites = new FileInfo(Environment.CurrentDirectory + "\\Favorites.bin");
            if (favorites.Exists)
            {
                using (BinaryReader reader = new BinaryReader(favorites.Open(FileMode.Open)))
                {
                    int numNames = reader.ReadInt32();
                    for (int i = 0; i < numNames; i++)
                        favoriteFilePathListBox.Items.Add(reader.ReadString());
                }
            }

            currentDirectoryTextBox.Text = Environment.CurrentDirectory;

            fileIcons = new ImageList();
            fileIcons.Images.Add(LevelEditor.Properties.Resources.folderIcon);
            fileIcons.Images.Add(LevelEditor.Properties.Resources.file);
            fileIcons.Images.Add(LevelEditor.Properties.Resources.modelIcon);
            fileIcons.Images.Add(LevelEditor.Properties.Resources.textureIcon);
            treeView1.ImageList = fileIcons;

            supportedFileFormats = new string[supportedTextureFormats.Length + supportedModelFormats.Length];
            supportedTextureFormats.CopyTo(supportedTextureFormats, 0);
            supportedModelFormats.CopyTo(supportedFileFormats, supportedTextureFormats.Length);

            // inilitize xna content
            services.AddService<Microsoft.Xna.Framework.Graphics.IGraphicsDeviceService>(new GraphicsDeviceService(graphics.device));
            contentBuilder = new MaterialEditor.ContentBuilder();
            content = new Microsoft.Xna.Framework.Content.ContentManager(services, contentBuilder.OutputDirectory);



            Matrix m = Matrix.CreateLookAt(Vector3.Backward * 20.0f, Vector3.Zero, Vector3.Up);


        }

        private void MainMenu_Load(object sender, EventArgs e)
        {
            // inilitize heightmap editor
            heightmapEditor = new HeightmapEditor();
            heightmapEditor.Owner = this;

            UpdateTypesBox();
        }

        void UpdateTypesBox()
        {
            DirectoryInfo dir = new DirectoryInfo(objectTypeHeaderLinkLabel.Text);
            using (StreamReader reader = new StreamReader(new FileStream(dir.FullName, FileMode.Open)))
            {
                string firstLine = reader.ReadLine();
                while (firstLine != "enum class ObjectID")
                {
                    firstLine = reader.ReadLine();
                }

                // skip the curly brace
                string line = reader.ReadLine();
                // start reading types
                while (line != "};")
                {
                    line = reader.ReadLine();
                    string[] data = line.Split(new char[] { ' ', '\t', ',' }, StringSplitOptions.RemoveEmptyEntries);
                    if (data.Length == 0)
                        continue;
                    line = data[0];
                    if (line != "};")
                        objectTypesListBox.Nodes.Add(line);
                }
            }
        }

        public void Inilitize()
        {
            // inilitize effects
            EffectManager.Inilitize();
        }

        void MainMenu_FormClosing(object sender, FormClosingEventArgs e)
        {
            Running = false;
            // write favorite file paths if there are any
            if (favoriteFilePathListBox.Items.Count > 0)
            {
                using (BinaryWriter writer = new BinaryWriter(new FileStream(Environment.CurrentDirectory + "\\Favorites.bin", FileMode.OpenOrCreate)))
                {
                    writer.Write(favoriteFilePathListBox.Items.Count);
                    for (int i = 0; i < favoriteFilePathListBox.Items.Count; i++)
                        writer.Write(favoriteFilePathListBox.Items[i] as string);
                }
            }
        }
        /// <summary>
        /// Updates the current scene
        /// </summary>
        public void Tick()
        {
            double now = DateTime.Now.TimeOfDay.TotalMilliseconds;
            double then = this.then.TotalMilliseconds;

            float dt = (float)(now - then) / 1000.0f;
            viewWindow1.Update();
            terrainTextureListBox.DataSource = terrainMaterials;
            if (heightmapEditor.Visible)
            {
                heightmapEditor.Tick(dt);
                heightmapEditor.Draw();
            }
        }

        /// <summary>
        /// Renders the current scene and displays it to the user
        /// </summary>
        public void Draw()
        {
            graphics.ClearBackBuffer(Color.CornflowerBlue);
            viewWindow1.Draw();

            graphics.Present();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        #region Browse Menu Stuff


        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            if (currentDirectory.Parent != null)
                ChangeWorkingDirectory(currentDirectory.Parent.FullName);
        }

        /// <summary>
        /// Update the files in the tree view
        /// </summary>
        private void UpdateFileList()
        {
            treeView1.Nodes.Clear();
            try
            {
                foreach (DirectoryInfo d in currentDirectory.GetDirectories())
                {
                    TreeNode n = new TreeNode(d.Name);
                    n.ImageIndex = 0;
                    treeView1.Nodes.Add(n);
                }
                foreach (FileInfo f in currentDirectory.GetFiles())
                {
                    TreeNode n = new TreeNode(f.Name);
                    n.ImageIndex = (int)BrowserImageType.Unrecognized;
                    if (supportedTextureFormats.Contains(f.Extension))
                        n.ImageIndex = (int)BrowserImageType.Texture;
                    else if (supportedModelFormats.Contains(f.Extension))
                        n.ImageIndex = (int)BrowserImageType.Model;
                    treeView1.Nodes.Add(n);
                }
            }
            catch (Exception ee)
            {
                MessageBox.Show(ee.Message);
            }
        }

        /// <summary>
        /// Modify the current working directory
        /// </summary>
        /// <param name="newDir">The directory to move the working directory to</param>
        private void ChangeWorkingDirectory(string newDir)
        {
            currentDirectoryTextBox.Text = newDir;
            previousDirectories.Push(currentDirectory);
            currentDirectory = new DirectoryInfo(newDir);
            UpdateFileList();
            if (previousDirectories.Count > 10)
            {
                previousDirectories.Reverse();
                previousDirectories.Pop();
                previousDirectories.Reverse();
            }
        }

        private void treeView1_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Node.ImageIndex == 0)
                ChangeWorkingDirectory(currentDirectory.FullName + '/' + e.Node.Text);
        }

        private void toolStripButton2_Click(object sender, EventArgs e)
        {
            if (previousDirectories.Count == 0)
                return;
            currentDirectory = previousDirectories.Pop();
            UpdateFileList();
        }

        private string[] supportedFileFormats;

        private string[] supportedTextureFormats = new string[]
        {
            ".png",
            ".tex",
            ".tga",
            ".jpg",
        };

        private string[] supportedModelFormats = new string[]
        {
            ".mdl",
            ".obj",
        };

        private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            treeView1.SelectedImageIndex = e.Node.ImageIndex;
            // show user options if they have selected a supported file format
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                string[] splitFilename = e.Node.Name.Split(new char[] { '.' });
                if (supportedFileFormats.Contains(splitFilename[splitFilename.Length - 1]))
                    browseMenuMenuStrip.Show(treeView1, e.Location);
            }
        }


        private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
        {

            if (treeView1.SelectedNode != null)
                treeView1.DoDragDrop(treeView1.SelectedNode.Text, DragDropEffects.Copy | DragDropEffects.Move);
        }

        private void currentDirectoryTextBox_Leave(object sender, EventArgs e)
        {
            DirectoryInfo d = new DirectoryInfo(currentDirectoryTextBox.Text);
            if (!d.Exists)
            {
                MessageBox.Show("The path " + d.FullName + " does not exist");
                return;
            }
            ChangeWorkingDirectory(currentDirectoryTextBox.Text);
        }
        private void viewWindow1_DragEnter(object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.Copy | DragDropEffects.Move;
        }

        private void viewWindow1_DragDrop(object sender, DragEventArgs e)
        {
            object data = e.Data.GetData(DataFormats.Serializable); 
            Point p = viewWindow1.PointToClient(new Point(e.X, e.Y));

            Vector3 p1 = viewWindow1.viewport.Unproject(new Vector3(p.X, p.Y, 0), Camera.instance.Projection, Camera.instance.View, Microsoft.Xna.Framework.Matrix.Identity);
            Vector3 p2 = viewWindow1.viewport.Unproject(new Vector3(p.X, p.Y, 1), Camera.instance.Projection, Camera.instance.View, Microsoft.Xna.Framework.Matrix.Identity);

            Vector3 direction = p2 - p1;
            direction.Normalize();

            Ray cameraRay = new Ray(p1, direction);
            Plane ground = new Plane(Vector3.Up, 1);

            float? intersects = cameraRay.Intersects(ground);
            if (intersects != null)
            {
                Vector3 position = cameraRay.Position + cameraRay.Direction * intersects.Value;
                if (data is Entity)
                {
                    Entity obj = data as Entity;
                    obj.Position = position;
                    viewWindow1.AddObject(obj);
                }
                else
                {
                    string filename = e.Data.GetData(DataFormats.Text) as string;
                    string[] filedata = filename.Split(new char[] { '.' });
                    string extention = filedata[filedata.Length - 1];
                    if (supportedModelFormats.Contains('.' + extention))
                    {
                        position.Y = 0;

                        SceneObject obj = new SceneObject("SCENE_OBJECT", currentDirectory.FullName + "\\" + filename);
                        obj.mesh.World = Matrix.CreateTranslation(position);

                        viewWindow1.AddObject(obj);                        
                    }
                }
            }
        }

        private void FavoriteButton_Click(object sender, EventArgs e)
        {
            // do nothing if the user is currently typeing
            if (currentDirectoryTextBox.Focused)
                return;

            favoriteFilePathListBox.Items.Add(currentDirectoryTextBox.Text);
        }

        private void favoriteFilePathListBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            // skip if no item is selected
            if (favoriteFilePathListBox.SelectedItem == null)
                return;
            // go to the selected path
            currentDirectoryTextBox.Text = favoriteFilePathListBox.SelectedItem as string;
            ChangeWorkingDirectory(currentDirectoryTextBox.Text);
        }
        #endregion

        #region heightmap stuff

        private void heightmapHeighest_ValueChanged(object sender, EventArgs e)
        {
            viewWindow1.terrain.HeightMap.heighest = (float)heightmapHeighest.Value;
        }

        private void hightmapLowest_ValueChanged(object sender, EventArgs e)
        {
            viewWindow1.terrain.HeightMap.lowest = (float)hightmapLowest.Value;
        }



        private void button1_Click(object sender, EventArgs e)
        {

        }

        public void UpdateHeightmap(Bitmap heightmap, float lowest, float heighest)
        {

        }


        private void heightmapResXValue_ValueChanged(object sender, EventArgs e)
        {
            UpdateHeightmap(heightmapPictureBox.Image as Bitmap, 0, 100);
        }

        private void togglePointsVisibleButton_Click(object sender, EventArgs e)
        {
            togglePointsVisibleButton.Checked = !togglePointsVisibleButton.Checked;
            viewWindow1.terrain.drawPoints = togglePointsVisibleButton.Checked;
        }

        private void wireframeToggleButton_Click(object sender, EventArgs e)
        {
            wireframeToggleButton.Checked = !wireframeToggleButton.Checked;
            viewWindow1.terrain.wireframe = wireframeToggleButton.Checked;
        }

        private void colorBox1_ColorChanged(object sender, EventArgs e)
        {
            viewWindow1.terrain.fernelColor = new Microsoft.Xna.Framework.Color(colorBox1.color.R, colorBox1.color.G, colorBox1.color.B);
        }
        #endregion

        #region texture stuff

        private void addTextureButton_Click(object sender, EventArgs e)
        {
            //OpenFileDialog dialog = new OpenFileDialog();
            //dialog.Filter = "Image Files | *.png;";
            //DialogResult result = dialog.ShowDialog();

            //if (result == System.Windows.Forms.DialogResult.OK)
            //{
            //    string name = Path.GetFileName(dialog.FileName).Split(new char[] { '.' })[0];
            //    viewWindow1.terrain.AddTexture(Microsoft.Xna.Framework.Graphics.Texture2D.FromStream(graphics.device, new FileStream(dialog.FileName, FileMode.Open)));
            //    terrainTextureListBox.Items.Add(name);
            //}
            string name = "texture" + terrainTextureListBox.Items.Count.ToString();
            TerrainMaterial mat = new TerrainMaterial(null, name, new BindingList<TerrainRule>());
            terrainMaterials.Add(mat);
            terrainMaterials.ResetBindings();
            TextureEditor texEditor = new TextureEditor(mat);
            texEditor.FormClosing += texEditor_FormClosing;
            texEditor.Show();
        }

        void texEditor_FormClosing(object sender, FormClosingEventArgs e)
        {
            terrainMaterials.ResetBindings();
        }

        private void viewWindow1_MouseDown(object sender, MouseEventArgs e)
        {

        }

        private void viewWindow1_MouseMove(object sender, MouseEventArgs e)
        {
            viewWindow1_MouseDown(sender, e);
        }

        private void removeTexture_Click(object sender, EventArgs e)
        {
            if (terrainTextureListBox.SelectedIndex != -1)
                terrainMaterials.Remove(terrainTextureListBox.SelectedItem as TerrainMaterial);
        }

        private void editHeightmapButton_Click(object sender, EventArgs e)
        {
            heightmapEditor.Visible = true;
        }

        private void editTextureButton_Click(object sender, EventArgs e)
        {
            if (terrainTextureListBox.SelectedIndex == -1)
                return;

            TextureEditor texEditor = new TextureEditor(terrainTextureListBox.SelectedItem as TerrainMaterial);
            texEditor.FormClosing += texEditor_FormClosing;
            texEditor.Show(this);
        }
        #endregion
        
        #region IO

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string filename = null;
            DialogResult result = System.Windows.Forms.DialogResult.None;
            if (IO.currentDirectory == null)
            {
                SaveFileDialog dialog = new SaveFileDialog();
                dialog.Filter = "Level files | *.lvl;";
                result = dialog.ShowDialog();
                if (result == System.Windows.Forms.DialogResult.OK)
                    filename = dialog.FileName;
            } 
            else
            {
                result = System.Windows.Forms.DialogResult.OK;
                filename = IO.currentDirectory;
            }
            
            if (result == System.Windows.Forms.DialogResult.OK)
            {
                IO.currentDirectory = Path.GetDirectoryName(filename);
                IO.Save(filename);
            }
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "Level files | *.lvl;";
            DialogResult result = dialog.ShowDialog();

            if (result == System.Windows.Forms.DialogResult.OK)
                IO.Save(dialog.FileName);
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "Level files | *.lvl";
            DialogResult result = dialog.ShowDialog();

            if (result == System.Windows.Forms.DialogResult.OK)
            {
                IO.Load(dialog.FileName);
                IO.currentDirectory = dialog.FileName;
            }
        }
        #endregion

        private void treeView1_MouseDown(object sender, MouseEventArgs e)
        {

        }

        private void objectTypesListBox_ItemDrag(object sender, ItemDragEventArgs e)
        {
            if (objectTypesListBox.SelectedNode != null)
                objectTypesListBox.DoDragDrop(new SceneObject(objectTypesListBox.SelectedNode.ToString(), Environment.CurrentDirectory + "/Content/Sphere.mdl"), DragDropEffects.Move | DragDropEffects.Copy);
        }


    }

    public class GraphicsDeviceService : Microsoft.Xna.Framework.Graphics.IGraphicsDeviceService
    {
        private Microsoft.Xna.Framework.Graphics.GraphicsDevice device;

        public GraphicsDeviceService(Microsoft.Xna.Framework.Graphics.GraphicsDevice device)
        {
            this.device = device;
        }

        public event EventHandler<EventArgs> DeviceCreated;

        public event EventHandler<EventArgs> DeviceDisposing;

        public event EventHandler<EventArgs> DeviceReset;

        public event EventHandler<EventArgs> DeviceResetting;

        public Microsoft.Xna.Framework.Graphics.GraphicsDevice GraphicsDevice
        {
            get { return device; }
        }
    }

    public delegate void BasicDelegate();
}
